#include <boost/lexical_cast.hpp>
#include <SDL/SDL.h>

#include "core/kernel.h"
#include "core/process.h"
#include "core/message.h"
#include "core/logger.h"
#include "core/engine.h"
#include "scene/scenenode.h"
#include "core/renderwindow.h"
#include "scene/camera.h"

namespace engine {

	namespace core {
		using engine::scene::SceneNode;
		using engine::core::RenderWindow;
		
		Kernel::Kernel() {
			Logger::getLogger("kernel")->setLevel(LL_DEBUG);
		}

		Kernel::~Kernel() {

		}

		bool Kernel::processSystemEvents() {
			SDL_Event event;

			while (SDL_PollEvent(&event)) {
				switch(event.type) {
					case SDL_QUIT: {
						killAllProcesses();
						return true;
					}
					break;
					case SDL_VIDEORESIZE: {
						m_ParentEngine->getRenderWindow()->resize(event.resize.w, event.resize.h);
					}
					break;
				}
			}
			
			
			//If we return true it means we are quitting the main loop!
			//NOT YET USED
			return false;
		}
		
		int Kernel::run() {
			Logger::getLogger("kernel")->debug("Beginning kernel run");
			
			for (ProcessList::iterator it =  m_ProcessList.begin(); 
					it != m_ProcessList.end(); ++it) {
		
				(*it)->initialize();
			}
			
			while (!m_ProcessList.empty()) {
				//First we pump the SDL message queue
				SDL_PumpEvents();

				for (ProcessList::iterator it =  m_ProcessList.begin(); 
						it != m_ProcessList.end(); ++it) {
				
					(*it)->prepare(getDT());
				}
								
				//Handle system events
				if (processSystemEvents())
					break; 
				
				//Get a list of all messages sent since the last processing
				list<shared_ptr<Process> >::iterator i = m_ProcessList.begin();
				for (; i != m_ProcessList.end(); ++i) {
					appendToMessageList((*i)->getOutbox());
					(*i)->clearOutbox();
				}

				//Process the messages
				processMessages();


				for (ProcessList::iterator it =  m_ProcessList.begin(); 
						it != m_ProcessList.end(); ++it) {
			
					(*it)->processInbox();
					(*it)->update();
				}
				
				m_ParentEngine->getRenderWindow()->clearBuffers();
				if (m_ParentEngine->getSceneManager()->getActiveCamera()) {
					m_ParentEngine->getSceneManager()->getActiveCamera()->use();
				}
				
				m_ParentEngine->getSceneManager()->getRootSceneNode()->update();
				m_ParentEngine->getRenderWindow()->swapBuffers();
			}
			
			for (ProcessList::iterator it =  m_ProcessList.begin(); 
					it != m_ProcessList.end(); ++it) {
		
				(*it)->shutdown();
			}
			
			Logger::getLogger("kernel")->debug("Finished run");
            return 0;
		}

		void Kernel::killAllProcesses() {
			Logger::getLogger("kernel")->info("Term signal received");
			for (ProcessList::iterator it = m_ProcessList.begin();
									it != m_ProcessList.end(); ++it) {
				m_ProcessesToKill.push_back((*it)->getPID());
			}			
		}
		
		void Kernel::processKernelMessage(shared_ptr<Message> msg) {
			Logger::getLogger("kernel")->debug("Kernel message found");
			switch(msg->m_Type) {
				case MT_KILL_PROCESS: {
					m_ProcessesToKill.push_back(msg->kill.m_PID);
				} break;
				case MT_TERM_SIGNAL: {
					killAllProcesses();
				} break;
			}
		}

		void Kernel::processMessages() {
			//
			
			while (!m_MessageList.empty()) {
				
				shared_ptr<Message> msg = m_MessageList.front();
				m_MessageList.pop_front();
			
				if (msg->m_MsgDest == 1) {
					processKernelMessage(msg);
				} else if (msg->m_MsgDest == 0) {
					//Send to all processes
					for (ProcessList::iterator it = m_ProcessList.begin();
											it != m_ProcessList.end(); ++it) {
							(*it)->receiveMessage(msg);
					}
				} else {
					if (m_Listeners.find(msg->m_Type) != m_Listeners.end()) {
						//Loop through the registered processes, sending the message
						
						for (vector<shared_ptr<Process> >::iterator it = m_Listeners[msg->m_Type].begin();
						it != m_Listeners[msg->m_Type].end(); ++it) {
								(*it)->receiveMessage(msg);
						}
					} 
					//Do nothing, no-one is registered for this message so leave it
				}
				
				for (vector<uint>::iterator it = m_ProcessesToKill.begin(); it != m_ProcessesToKill.end(); ++it) {
					removeProcess((*it));
				}
			}
		}

		void Kernel::registerListener(uint msgID, uint PID) {
            m_Listeners[msgID].push_back(findProcessByID(PID));
		}

		shared_ptr<Process> Kernel::findProcessByID(uint PID) {
            ///Finds returns a pointer to the process which has the passed ID
            ///returns a NULL shared_ptr on failure

            if (m_ProcessesByID.find(PID) != m_ProcessesByID.end()) {
                return m_ProcessesByID[PID];
            }

            return shared_ptr<Process>();
		}

		uint Kernel::addProcess(shared_ptr<Process> newProc) {
            uint newID = generatePID();
            newProc->setPID(newID);
            m_ProcessList.insert(m_ProcessList.end(), newProc);
            m_ProcessesByID[newID] = newProc;
		
            return newID;
		}

		void Kernel::removeProcess(uint PID) {
		    //Find the process in the list using the map to get the process from the ID
            ProcessList::iterator it = std::find(m_ProcessList.begin(),
                                                    m_ProcessList.end(),
                                                    m_ProcessesByID[PID]);

            //Remove the process from both containers
            m_ProcessList.erase(it);
            m_ProcessesByID.erase(PID);
		}
		
		void Kernel::setParentEngine(Engine* parent) {
			m_ParentEngine = parent;
		}
	}

}
